<template>
  <div class="container">
    <div class="left"></div>
    <div class="fbx-container" ref="threeContainerRef"></div>
    <div class="right"></div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { Vector2 } from 'three';

let width = 0;
let height = 0;
const threeContainerRef = ref<HTMLDivElement>();

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 3000);
camera.position.set(1000, 300, 1000);
camera.lookAt(0, 0, 0);
// scene.add(camera); 不需要添加

const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);

//   添加光源
const light = new THREE.AmbientLight(0xffffff, 1.0);
light.intensity = 300.0; //光照强度
// light.decay = 0.0; //设置 点 光源不随距离衰减
light.position.set(1000, 1000, 1000);
scene.add(light);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(0xcccccc);
renderer.render(scene, camera);

const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', () => {
  renderer.render(scene, camera);
});

onMounted(() => {
  // 只有页面加载完成后才能进行渲染
  startRender();

  testAddMesh();

  loadFBX();
});

const testAddMeshArray: THREE.Object3D[] = [];
const testAddMesh = () => {
  const geometry2 = new THREE.BoxGeometry(200, 200, 200);
  const material2 = new THREE.MeshBasicMaterial({ color: 0x008800, transparent: true, opacity: 0.5 });
  const testmesh = new THREE.Mesh(geometry2, material2);

  testAddMeshArray.push(testmesh);
  scene.add(testmesh);
};

const startRender = () => {
  width = threeContainerRef.value.clientWidth;
  height = threeContainerRef.value.clientHeight;

  renderer.setSize(width, height);
  threeContainerRef.value.appendChild(renderer.domElement);
};

//   加载 FBX 模型
const loadFBX = () => {
  const loader = new FBXLoader();
  loader.load(
    '/black_box.fbx',
    (group) => {
      console.log(group);

      if (group.children.length > 0) {
        let mesh = group.children[0];
        // 改变相机观察目标
        camera.aspect = width / height;
        camera.lookAt(mesh.position);
        camera.updateProjectionMatrix();

        // 注意相机控件OrbitControls会影响lookAt设置，注意手动设置OrbitControls的目标参数
        controls.target = mesh.position;
        controls.update();

        mesh.add(axesHelper.clone());
      }

      // 添加最外层加载出来的模型 由于setAnimationLoop 就不需要重新render了
      scene.add(group);
      renderer.render(scene, camera);

      addOutline(width, height, group);
    },
    (event) => {
      console.log((event.loaded / event.total) * 100 + '% loaded');
    },
    (error) => {
      console.log(error);
    },
  );
};

// equestAnimationFrame(); renderer.setAnimationLoop();这两个方法会影响描边的效果。
const addOutline = (width: number, height: number, model: THREE.Object3D) => {
  const composer = new EffectComposer(renderer);
  const renderPass = new RenderPass(scene, camera);
  const outlinePass = new OutlinePass(new Vector2(width, height), scene, camera);
  //模型描边颜色，默认白色
  outlinePass.visibleEdgeColor.set(0xffff00);
  //高亮发光描边厚度
  outlinePass.edgeThickness = 4;
  //高亮描边发光强度
  outlinePass.edgeStrength = 6;
  //模型闪烁频率控制，默认0不闪烁
  outlinePass.pulsePeriod = 2;
  composer.addPass(renderPass);
  composer.addPass(outlinePass);
  // composer.render();

  renderer.domElement.addEventListener('click', function (event) {
    // .offsetY、.offsetX以canvas画布左上角为坐标原点,单位px
    const px = event.offsetX;
    const py = event.offsetY;
    //屏幕坐标px、py转WebGL标准设备坐标x、y
    //width、height表示canvas画布宽高度
    const x = (px / width) * 2 - 1;
    const y = -(py / height) * 2 + 1;
    //创建一个射线投射器`Raycaster`
    const raycaster = new THREE.Raycaster();
    //.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
    // 形象点说就是在点击位置创建一条射线，射线穿过的模型代表选中
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
    //.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
    // 未选中对象返回空数组[],选中一个对象，数组1个元素，选中两个对象，数组两个元素
    const intersects = raycaster.intersectObjects([model, ...testAddMeshArray]);
    console.log('射线器返回的对象', intersects);
    // intersects.length大于0说明，说明选中了模型
    if (intersects.length > 0) {
      console.log('选中对象', intersects[0].object);
      // 选中模型的第一个模型，设置为红色
      outlinePass.selectedObjects = [intersects[0].object];
      composer.render();
    }
  });
};
</script>

<style scoped lang="less">
.container {
  width: 100%;
  height: 80vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
.left {
  width: 10px;
  height: 800px;
  border: 1px solid black;
}
.right {
  width: 10px;
  height: 800px;
  border: 1px solid black;
}
.fbx-container {
  width: 100%;
  height: 1000px;
  border: 1px solid red;
}
</style>
